home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 April: Mac OS SDK / Dev.CD Apr 00 SDK1.toast / Development Kits / Mac OS / Open Transport 1.3 / Open Transport SDK / Open Tpt Module Developer / Samples / DLPI Template / loopback.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-30  |  18.8 KB  |  721 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        loopback.c
  3.  
  4.     Contains:     * Simple loopback driver utilizing Mentat DLPI Template Code (dlpiether.c)
  5.                  * This file, combined with dlpiether.c (or linked against the Shared LIbrary
  6.                  * containing dlpiether.c) is a functioning loopback driver for Open Transport.
  7.                  * This file also serves as a template for writing the hardware-specific
  8.                  * code for a driver which will use dlpiether.c.  Comments in the code and
  9.                  * the accompanying documentation, Mentat DLPI Driver Template, provide
  10.                  * necessary information.
  11.  
  12.              ** loopback.c 1.2 Last Changed 29 Mar 1996
  13.  
  14.     Copyright:    © 1996 by Mentat Inc.
  15.  
  16.     To Do:
  17. */
  18.  
  19. static char    sccs_loop_id[] = "@(#)loopback.c    1.2";
  20.  
  21. /*
  22.  * "BoardX:" in comments refers to a typical hardware driver.  These are hints
  23.  * and suggestions for converting this code to an actual hardware driver.
  24.  */
  25. #include <OpenTptModule.h>
  26. #include <dlpi.h>
  27. #include "NewDevices.h"
  28. #include <NameRegistry.h>
  29. #include <OpenTptLinks.h>
  30. #include <OpenTptPCISupport.h>
  31. #include <dlpiuser.h>
  32. #include <dlpiether.h>
  33.  
  34. /*
  35.  * The following defines are inserted here for convenience for the 
  36.  * loopback driver.  A "real" driver may place these in separate header
  37.  * files and/or use system-defined values.
  38.  */
  39. #define LOOPBACK_DRIVER        /* conditionally compiles loopback code */
  40.  
  41. /* Some useful MacBug debugging macros */
  42.  
  43. #define mps_printf        OTKernelPrintfForMentat
  44. #define loop_error(a)        mps_printf a
  45. #define loop_trace(a)        mps_printf a
  46. #define loop_debug(a)        if(loop_debug) {mps_printf a;} else {;}
  47. #define loop_debug2(a)        if(loop_debug >= 2) {mps_printf a;} else {;}
  48. #define    trace_args    "  "        
  49. #define    trace0        "0 "    /* Fatal */
  50. #define    trace1        "1 "    /* General */
  51. #define    trace2        "2 "    /* Out of Memory */
  52. #define    trace3        "3 "    /* Event */
  53. #define    trace_eol        
  54. extern    int    OTKernelPrintfForMentat(char * fmt, ...);
  55. static    int    loop_debug = 1;
  56.  
  57. #define    MAX_PACKET_SIZE    1500    /* We're ethernet, remember :-) */
  58. #define    MIN_PACKET_SIZE    60    /* We'll pad short packets Ethernet style */
  59. #define LOOP_HIWAT    2048    /* Flow control high water mark */
  60. #define LOOP_LOWAT    64    /* Flow control low water mark */
  61.  
  62. /* just so DL_INFO_ACK has something to report */
  63. static    unsigned char    my_hardware_addr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
  64.  
  65. /*
  66.  * See OpnTptLinks.h for more
  67.  */
  68. #define kLoopbackModuleID    7199        /* STREAMS module_info mi_idnum */
  69. #define kLoopbackName        "loopback"    /* mi_idname */
  70.  
  71.  
  72. /* Driver information 'per board', e.g., internal control structure. */
  73. typedef struct board_s {
  74.     dle_t    board_dle;        /* Must be first in structure. */
  75.     /* BoardX: Fields needed for controlling hardware go here */
  76. } board_t;
  77.  
  78. /* 
  79.  * STREAMS instance data (q_ptr) 
  80.  */
  81. typedef struct loop_s {
  82.     dcl_t    loop_dcl;        /* Must be first in structure */
  83.     /* BoardX: Any 'per stream' fields needed by board go here */
  84. } loop_t;
  85. #define    loop_dle    loop_dcl.dcl_hw        /* pointer to board_t structure */
  86.  
  87. static    void    loop_bcopy_to_noncached(unsigned char * src, unsigned char * dst
  88.             , unsigned int count);
  89. static    int    loop_close(queue_t * q, int flag, cred_t * credp);
  90. static    void    loop_hw_address_filter_reset(void * loopvp, dle_addr_t * addr_list
  91.             , ulong addr_count, ulong promisc_count
  92.             , ulong multi_promisc_count, ulong accept_broadcast
  93.             , ulong accept_error);
  94. static    void    loop_hw_start(void * loopvp);
  95. static    void    loop_hw_stop(void * loopvp);
  96. static    int    loop_open(queue_t * q, dev_t * devp, int flag, int sflag
  97.             , cred_t * credp);
  98. static    mblk_t    * loop_reallocb(mblk_t * mp, int new_size);
  99. static    int    loop_rsrv(queue_t * q);
  100. static    int    loop_wput(queue_t * q, mblk_t * mp);
  101. static    mblk_t    * loop_wput_process(queue_t * q, mblk_t * mp, long * total);
  102.  
  103. /*
  104.  * Global pointer to the board_t structure for this device instantiation.  This
  105.  * structure is allocated in the InitStreamModule routine and freed in
  106.  * TerminateStreamModule.  CFM guarantees us a separate data space for all
  107.  * instances of the device, so this pointer is always unique.
  108.  */
  109. static    board_t    * board_global;
  110.  
  111. /* STREAMS configuration structures. */
  112. static struct module_info info =  {
  113.     kLoopbackModuleID, kLoopbackName
  114.     , MIN_PACKET_SIZE, MAX_PACKET_SIZE
  115.     , LOOP_HIWAT, LOOP_LOWAT
  116. };
  117.  
  118. /* 
  119.  * STREAMS qinit structures.  Drivers have no rput.
  120.  */
  121. static struct qinit rinit = {
  122.     NULL, loop_rsrv, loop_open, loop_close, NULL, &info
  123. };
  124.  
  125. static struct qinit winit = {
  126.     loop_wput, NULL, loop_open, loop_close, NULL, &info
  127. };
  128.  
  129. struct streamtab loopback_info = { &rinit, &winit };
  130.  
  131. /* 
  132.  * BoardX:  Hardware drivers would typically add 'kOTModUsesInterrupts' to
  133.  * flags entry of following.
  134.  */
  135. static install_info    loopback_install_info =
  136. {
  137.     &loopback_info,
  138.     kOTModIsDriver | kOTModUpperIsDLPI,
  139.     SQLVL_MODULE,
  140.     NULL,
  141.     0,
  142.     0
  143. };
  144.  
  145. #ifndef LOOPBACK_DRIVER
  146. static void
  147. board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count)
  148. {
  149.     unsigned int iterations;
  150.  
  151.     /* Do short copies with a simple loop. */
  152.     if (count <= 15) {
  153.         do {
  154.             --count;
  155.             *dst++ = *src++;
  156.         } while (count);
  157.         return;
  158.     }
  159.  
  160.     /* Align on 8 byte dst boundary */
  161.     iterations = (8 - ((unsigned int)dst & 0x7)) & 0x7;
  162.     count -= iterations;
  163.     if (iterations) {
  164.         do {
  165.             --iterations;
  166.             *dst++ = *src++;
  167.         } while (iterations);
  168.     }
  169.  
  170.     /* Copy 32 byte chunks */
  171.     iterations = count >> 5;
  172.     count &= 0x1F;
  173.     if (iterations) {
  174.         double * dsrc = (double *)src;
  175.         double * ddst = (double *)dst;
  176.         do {
  177.             iterations--;
  178.             ddst[0] = dsrc[0];
  179.             ddst[1] = dsrc[1];
  180.             ddst[2] = dsrc[2];
  181.             ddst[3] = dsrc[3];
  182.             ddst += 4;
  183.             dsrc += 4;
  184.         } while (iterations);
  185.         src = (unsigned char *)dsrc;
  186.         dst = (unsigned char *)ddst;
  187.     }
  188.  
  189.     /* Copy 4 byte chunks */
  190.     iterations = count >> 2;
  191.     count &= 0x3;
  192.     if (iterations) {
  193.         unsigned long * lsrc = (unsigned long *)src;
  194.         unsigned long * ldst = (unsigned long *)dst;
  195.         do {
  196.             --iterations;
  197.             *ldst++ = *lsrc++;
  198.         } while (iterations);
  199.         src = (unsigned char *)lsrc;
  200.         dst = (unsigned char *)ldst;
  201.     }
  202.  
  203.     /* Copy the rest */
  204.     while (count--)
  205.         *dst++ = *src++;
  206. }
  207.  
  208. /*
  209.  * Template interrupt routine for processing inbound packets.
  210.  */
  211. long board_intr ()
  212. {
  213.     board_t    * board = board_global;
  214.     void    * cookie;
  215.     dle_t    * dle = &board->board_dle;
  216.     mblk_t    * mp;
  217.  
  218.     OTEnterInterrupt();
  219.  
  220.     /* Handle transmit done interrupts, if necessary. */
  221.  
  222.     /* Handle receive interrupts. */
  223.     while (receive_packets_available) {
  224.         /* Perhaps use esballoc() to avoid data copy? */
  225.         mp = allocb(packet_length, BPRI_LO);
  226.         if (!mp) {
  227.             dle->dle_istatus.receive_discards++;
  228.             continue;
  229.         }
  230.  
  231.         /* Copy the data into mp->b_rptr. */
  232.  
  233.         mp->b_wptr = mp->b_rptr + packet_length;
  234.  
  235.         /* Deliver the packet. */
  236.         dle_inbound(dle, mp);
  237.     }
  238.  
  239.     OTLeaveInterrupt();
  240.     return 1;
  241. }
  242. #endif    /* LOOPBACK_DRIVER */
  243.  
  244. /* This function must be exported; see loop.exp */
  245. install_info *
  246. GetOTInstallInfo ()
  247. {
  248.     loop_debug((trace_args "GetOTInstallInfo" trace_eol));
  249.     return &loopback_install_info;
  250. }
  251.  
  252. Boolean
  253. InitStreamModule (void * systemDependent)
  254. {
  255.     board_t    * board;
  256.     dle_t    * dle;
  257.  
  258.     loop_debug((trace_args "InitStreamModule" trace_eol));
  259.  
  260.     /* Allocate the board_t structure for this device */
  261.     board = (board_t *)OTAllocMem(sizeof(board_t));
  262.     if (!board)
  263.         return false;
  264.  
  265.     /* These are the hardware start/stop/reset functions */
  266.     bzero((char *)board, sizeof(board_t));
  267.     dle = &board->board_dle;
  268.     dle->dle_hw.dlehw_start = loop_hw_start;
  269.     dle->dle_hw.dlehw_stop = loop_hw_stop;
  270.     dle->dle_hw.dlehw_address_filter_reset = loop_hw_address_filter_reset;
  271.     dle->dle_hw.dlehw_send_error = NULL;
  272.     dle->dle_hw.dlehw_recv_error_flags = 0;
  273.  
  274.     /*
  275.      * Suggestions:
  276.      * - Install interrupt vectors in case any memory allocations are
  277.      *    required.  Interrupts should not be enabled until the
  278.      *    board start routine is called.
  279.      * - Reset the hardware to a known state.  If the hardware does
  280.      *    not respond, then return false.
  281.      * - Read the Ethernet address from the board's ROM area.  This
  282.      *    address should be copied into both the dle_factory_addr
  283.      *    field and the dle_current_addr field.
  284.      */
  285.     bcopy(my_hardware_addr, dle->dle_factory_addr, 6);
  286.     bcopy(my_hardware_addr, dle->dle_current_addr, 6);
  287.  
  288.     /*
  289.      * Allow the DLPI common code to initialize the dle fields. Once
  290.      * dle_init is called, dle_terminate must be called before freeing
  291.      * the dle structure.  There is private memory allocated for each
  292.      * dle that needs to be freed.
  293.      */
  294.     dle_init(dle, 0);
  295.     board_global = board;
  296.  
  297.     return true;
  298. }
  299.  
  300. void
  301. TerminateStreamModule (void)
  302. {
  303.     board_t    * board = board_global;
  304.  
  305.     loop_debug((trace_args "TerminateStreamModule" trace_eol));
  306.  
  307.     /*
  308.      * Suggestions:
  309.      * - Remove interrupt vectors.
  310.      * - Reset the hardware to a known state.
  311.      */
  312.  
  313.     board_global = (board_t *)NULL;
  314.     dle_terminate(&board->board_dle);
  315.     OTFreeMem(board);
  316. }
  317.  
  318. OTResult
  319. ValidateHardware (RegEntryID * our_id)
  320. {
  321.     loop_debug((trace_args "ValidateHardware" trace_eol));
  322.     return kOTNoError;    
  323. }
  324.  
  325. /* STREAMS close routine. */
  326. static int
  327. loop_close (queue_t * q, int flag, cred_t * credp)
  328. {
  329.     loop_debug((trace_args "loop_close(%x), dcl_dle %x" trace_eol
  330.         , q, ((dcl_t *)q->q_ptr)->dcl_hw ));
  331.     /*
  332.      * NOTE: there is probably not much else that needs to be done
  333.      * in this routine.  If you need to know about the number of
  334.      * open instances, dle_refcount is the number of streams still
  335.      * referencing the device (decremented in dle_close).
  336.      */
  337.     return dle_close(q);
  338. }
  339.  
  340. static void
  341. loop_hw_address_filter_reset (void * boardvp, dle_addr_t * addr_list
  342.             , ulong addr_count, ulong promisc_count
  343.             , ulong multi_promisc_count, ulong accept_broadcast
  344.             , ulong accept_error)
  345. {
  346.     board_t        * board = boardvp;
  347.     dle_t        * dle = &board->board_dle;
  348.     dle_addr_t    * dlea;
  349.     unsigned char    * first_phys_addr = 0;
  350.     uint        phys_addr_count = 0;
  351.  
  352.     /* Calculate the new logical address filter for multicast addresses. */
  353.     for (dlea = addr_list; dlea; dlea = dlea->dlea_next) {
  354.         if (dlea->dlea_addr[0] & 0x1) {
  355.             /*
  356.              * If the address is a multicast address, then set
  357.              * the right bits in the new filter.
  358.              */
  359.         } else {
  360.             /*
  361.              * Additional physical address.  This does not happen
  362.              * with the first version of the DLPI template code.
  363.              * However, at some future time, we may want to
  364.              * support multiple physical addresses on one
  365.              * hardware tap.
  366.              */
  367.             if (!first_phys_addr)
  368.                 first_phys_addr = dlea->dlea_addr;
  369.             phys_addr_count++;
  370.         }
  371.     }
  372.  
  373.     if (first_phys_addr) {
  374.         /*
  375.          * If there were any physical, non-multicast addresses in
  376.          * the list, then check to see if the first one is different
  377.          * from the current one.  If so, copy the new address into
  378.          * dle_current_addr and take whatever steps are necessary
  379.          * with the hardware to change the physical address.
  380.          */
  381.         ;
  382.     }
  383.  
  384.     /*
  385.      * Compare the new address filter with the old one.  If the new
  386.      * one is different, then set the hardware appropriately.
  387.      */
  388.  
  389.     /*
  390.      * If there are multiple physical addresses, then the board
  391.      * probably needs to be put in a promiscuous state.
  392.      */
  393.     if (phys_addr_count > 1)
  394.         promisc_count |= 1;
  395.  
  396.     if (promisc_count || multi_promisc_count)
  397.         /* Set the board into promiscuous mode. */;
  398.     else
  399.         /* Clear any promiscuous mode that may be set*/;
  400.     /*
  401.      * Save the promiscuous setting in the board structure so that
  402.      * updates to the hardware registers from loop_start or other
  403.      * routines will be correct.
  404.      */
  405. }
  406.  
  407. /* Called by dlpiether code through dlehw_start. */
  408. static    void
  409. loop_hw_start (void * boardvp)
  410. {
  411.     board_t    * board = boardvp;
  412.  
  413.     /* Kick the board alive and allow receive interrupts. */
  414. }
  415.  
  416. /* Called by dlpiether code through dlehw_stop. */
  417. static    void
  418. loop_hw_stop (void * boardvp)
  419. {
  420.     /* Turn off interrupts and leave the hardware disabled. */
  421. }
  422.         
  423.  
  424. /* STREAMS open routine. */
  425. static    int
  426. loop_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp)
  427. {
  428.     int    ret_code;
  429.  
  430.     loop_debug((trace_args "loop_open()" trace_eol));
  431.     /*
  432.      * This routine probably does not need to do anything other
  433.      * than call dle_open.  dle_refcnt is incremented.
  434.      */
  435.     ret_code = dle_open(&board_global->board_dle, q, devp, flag, sflag, credp
  436.             , sizeof(loop_t));
  437.     loop_debug((trace_args "dle_open(%x) = %d, dcl_hw %x" trace_eol
  438.         , &board_global->board_dle
  439.         , ret_code
  440.         , ((dcl_t *)q->q_ptr)->dcl_hw ));
  441.     return ret_code;
  442. }
  443.  
  444. /*
  445.  * Function used only by loopback driver; allocate or re-allocate
  446.  * a message block to assure minimum size.
  447.  */
  448. #ifdef LOOPBACK_DRIVER
  449. static mblk_t *
  450. loop_reallocb (mblk_t * mp, int new_size)
  451. {
  452.     mblk_t        * mp1;
  453.     dblk_t        * db;
  454.     int        our_size;
  455.     int        need;
  456.     unsigned char    * base;
  457.  
  458.     our_size = mp->b_wptr - mp->b_rptr;
  459.     need = (new_size > our_size) ? new_size : our_size;
  460.     db = mp->b_datap;
  461.     base = db->db_base;
  462.  
  463.     if (db->db_ref == 1) {
  464.         if ((db->db_lim - mp->b_rptr) >= need)
  465.             return mp;
  466.         if ((db->db_lim - base) >= need) {
  467.             BlockMoveData((char *)mp->b_rptr, (char *)base, our_size);
  468.             mp->b_rptr = base;
  469.             mp->b_wptr = base + our_size;
  470.             return mp;
  471.         }
  472.     }
  473.     mp1 = allocb(need, BPRI_MED);
  474.     if (mp1) {
  475.         mp1->b_wptr = mp1->b_rptr + our_size;
  476.         mp1->b_datap->db_type = db->db_type;
  477.         mp1->b_cont = mp->b_cont;
  478.         bcopy((char *)mp->b_rptr, (char *)mp1->b_rptr, our_size );
  479.     }
  480.     freeb(mp);
  481.     return mp1;
  482. }
  483. #endif    /* LOOPBACK_DRIVER */
  484.  
  485. /* 
  486.  * Read-side service routine.
  487.  * Pass all inbound packets upstream.  Messages are placed on
  488.  * the read-side queue by dle_inbound.  Unless the hardware
  489.  * requires something special, no additional code should be
  490.  * required.  NOTE: Additional messages may be added to the
  491.  * queue while this routine is running. If canputnext() fails,
  492.  * messages are freed rather than put back on the queue.  
  493.  * This is necessary since dle_inbound() will continue to add 
  494.  * messages without checking flow control (it can't call canputnext()
  495.  * from interrupt context).
  496.  */
  497. static int
  498. loop_rsrv (q)
  499.     queue_t    * q;
  500. {
  501.     mblk_t * mp;
  502.  
  503.     while (mp = getq(q)) {
  504.         /*
  505.          * Private message to be passed back to the dlpiether
  506.          * code.  This interface is required for supporting
  507.          * 802.2 XID and Test packets.
  508.          */
  509.         if (mp->b_datap->db_type == M_CTL) {
  510.             dle_rsrv_ctl(q, mp);
  511.         } else if (canputnext(q))
  512.             putnext(q, mp);
  513.         else {
  514.             freemsg(mp);
  515.             flushq(q, FLUSHDATA);
  516.             break;
  517.         }
  518.     }
  519.     return 0;
  520. }
  521.  
  522. /* Write-side put routine. Only handles M_DATA, handing others to dlpiether. */
  523. static int
  524. loop_wput (queue_t * q, mblk_t * mp)
  525. {
  526.     loop_t    * loop;
  527.     mblk_t    * first_mp;
  528.     long    len;
  529.     long    remaining;
  530.     long    total;
  531.     unsigned char    * xmt_buf;
  532.  
  533.     loop = (loop_t *)q->q_ptr;
  534.     if (mp->b_datap->db_type != M_DATA) {
  535.         mp = dle_wput(q, mp);
  536.         if (!mp)
  537.             return 0;
  538.         switch (mp->b_datap->db_type) {
  539.         case M_DATA:
  540.             break;        /* it's ready to send */
  541.         case M_IOCNAK:
  542.             /*
  543.              * Any driver private ioctl's come back from
  544.              * dle_wput() as an M_IOCNAK with ioc_error
  545.              * set to EINVAL; the rest of the original M_IOCTL
  546.              * is intact (including ioc_cmd & trailing M_DATAs).
  547.              * It may be processed here.
  548.              */
  549.              qreply(q,mp);
  550.              return 0;
  551.         default:
  552.             /* dle_wput() has formatted the reply for us */
  553.             qreply(q, mp);
  554.             return 0;
  555.         }
  556.     }
  557.  
  558.     /* Transmit the packet defined by mp->b_rptr to mp->b_wptr. */
  559.  
  560. #ifndef LOOPBACK_DRIVER
  561.     if (Can't transmit for some reason) {
  562.         ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
  563.         freemsg(mp);
  564.         return 0;
  565.     }
  566.     /*
  567.      * Copy the packet to transmit buffer.  This is an example
  568.      * showing the copies and the calculation of the length
  569.      * of the packet.  Code for actual hardware devices will
  570.      * obviously need to be updated, at least to initialize
  571.      * xmt_buf to point to the hardware transmit area.
  572.      */
  573.     remaining = MAX_PACKET_SIZE;
  574.     first_mp = mp;
  575.     xmt_buf = ??;        /* BoardX: replace this */
  576.     do {
  577.         unsigned char    * rptr = mp->b_rptr;
  578.         int    len = mp->b_wptr - rptr;
  579.         if (len <= 0)
  580.             continue;
  581.         if (remaining < len) {
  582.             /* packet too large */
  583.             mp = dle_wput_ud_error(first_mp, DL_UNDELIVERABLE, 0);
  584.             if (mp)
  585.                 qreply(q, mp);
  586.             return 0;
  587.         }
  588.         remaining -= len;
  589.         xmt_buf += len;
  590.         board_bcopy_to_noncached(rptr, xmt_buf - len, len);
  591.     } while ((mp = mp->b_cont)  &&  remaining);
  592.  
  593.     total = MAX_PACKET_SIZE - remaining;
  594.  
  595.     /* Fill in the 802 length field if it is zero. */
  596.     {
  597.     unsigned char    * up;
  598.     up = &xmt_buf[-total];
  599.     if (!(up[12] | up[13])) {
  600.         uint    adjusted_total = total - 14;
  601.         up[12] = (unsigned char)(adjusted_total >> 8);
  602.         up[13] = (unsigned char)(adjusted_total & 0xff);
  603.     }
  604.     }
  605.  
  606.     if (total < MIN_PACKET_SIZE) {
  607.         /*
  608.          * If the packet is less than the minimum transmit
  609.          * size, then you need to pad the packet.  Hopefully
  610.          * this is just of matter of setting the hardware
  611.          * to pad automatically.
  612.          */
  613.          ;
  614.     }
  615.     
  616.     /* Trigger the hardware transmit. */
  617. #else
  618.     /* Loopback driver processing */
  619.     first_mp = loop_wput_process(q, mp, &total);
  620.     if (!first_mp) {
  621.         ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
  622.         return 0;
  623.     }
  624.     
  625. #endif
  626.     /* Increment the appropriate MIB interface statistics. */
  627.     ((dle_t *)loop->loop_dle)->dle_istatus.bytes_sent += total;
  628.     if (first_mp->b_rptr[0] & 0x1) {
  629.         unsigned char    * rptr = first_mp->b_rptr;
  630.         if ((rptr[0] & rptr[1] & rptr[2] & rptr[3] & rptr[4]
  631.              & rptr[5]) == 0xFF)
  632.             ((dle_t *)loop->loop_dle)->dle_istatus.broadcast_frames_sent++;
  633.         else
  634.             ((dle_t *)loop->loop_dle)->dle_istatus.multicast_frames_sent++;
  635.     } else
  636.         ((dle_t *)loop->loop_dle)->dle_istatus.unicast_frames_sent++;
  637.  
  638. #ifndef LOOPBACK_DRIVER
  639.     freemsg(first_mp);
  640. #else
  641.     /*
  642.      * Loop it around - dle_inbound() call's putq(), so stack depth not
  643.      * an issue here.
  644.      */
  645.     dle_inbound((dle_t *)loop->loop_dle, first_mp);
  646.     return 0;
  647. #endif
  648. }
  649.  
  650. /*
  651.  * For the loopback driver, we turn around the message, filling in 802
  652.  * length/type field.  This function will not be used by hardware code
  653.  * modifying this template.
  654.  */
  655. #ifdef LOOPBACK_DRIVER
  656. static mblk_t *
  657. loop_wput_process (queue_t * q, mblk_t * mp, long * total)
  658. {
  659.     long        len;
  660.     long        first_len;
  661.     mblk_t        * mp1;
  662.     mblk_t        * mp2;
  663.     unsigned char    * up;
  664.  
  665.     first_len = mp->b_wptr - mp->b_rptr;
  666.     *total = first_len;
  667.     for (mp1 = mp; mp2 = mp1->b_cont; ) {
  668.         len = mp2->b_wptr - mp2->b_rptr;
  669.         if (len <= 0  ||  mp2->b_datap->db_type != M_DATA) {
  670.             mp1->b_cont = mp2->b_cont;
  671.             freeb(mp2);
  672.             continue;
  673.         }
  674.         mp1 = mp2;
  675.         *total += len;
  676.     }
  677.  
  678.     if (*total > MAX_PACKET_SIZE) {
  679.         mp = dle_wput_ud_error(mp, DL_UNDELIVERABLE, 0);
  680.         if (mp)
  681.             qreply(q, mp);
  682.         return NULL;
  683.     }
  684.     if (*total < MIN_PACKET_SIZE) {    /* we need to pad */
  685.         if (mp->b_cont && !pullupmsg(mp, -1)) {
  686.             mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
  687.             if (mp)
  688.                 qreply(q, mp);
  689.             return NULL;
  690.         }
  691.         mp = loop_reallocb(mp, MIN_PACKET_SIZE);
  692.         if (!mp)
  693.             return NULL;
  694.  
  695.         bzero((char *)mp->b_wptr, (MIN_PACKET_SIZE - *total));
  696.         mp->b_wptr += (MIN_PACKET_SIZE - *total);
  697.         first_len = MIN_PACKET_SIZE;
  698.     }
  699.     /*
  700.      * db_ref note:  db_ref may be > 1 for some packets (we know some
  701.      * IP packets will have db_ref > 1).  We assume that the portion 
  702.      * of the packet we are jamming is opaque to upper levels and since
  703.      * we always write it here, things should be fine.  If db_ref > 2,
  704.      * this could be a problem, but this we know is 'never' true.
  705.      */
  706.     if (first_len < 14 && !pullupmsg(mp, 14)) {
  707.         mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
  708.         if (mp)
  709.             qreply(q, mp);
  710.         return NULL;
  711.     }
  712.     up = &mp->b_rptr[12];
  713.     if (!(up[12] | up[13])) {
  714.         uint    adjusted_total = *total - 14;
  715.         up[12] = (unsigned char)(adjusted_total >> 8);
  716.         up[13] = (unsigned char)(adjusted_total & 0xff);
  717.     }
  718.     return mp;
  719. }
  720. #endif    /* LOOPBACK_DRIVER */
  721.